import * as fs from 'fs';
import * as path from 'path';


const fns: any = {};

export { fns };
    
export function loadFns(dir: string, prefix?: string, ignores?: (string | RegExp)[]) { 
    if (!prefix) { 
        prefix = '/';
    }
    let files = fs.readdirSync(dir);
    for (let f of files) {
        let filename = f.replace(/(\.js)|(\.ts)$/, '');
        //如果在ignore列表，直接忽略
        // console.debug(filename, isIgnore(filename, ignores));
        if (isIgnore(filename, ignores)) { 
            continue;
        }

        let realpath = path.join(dir, f);
        let stat = fs.statSync(realpath);
        //递归加载文件夹
        if (stat.isDirectory()) { 
            loadFns(path.join(dir, f), prefix+'/'+ f, ignores);
            continue;
        }

        //加载具体文件
        let Cls = require(path.join(dir, filename));
        if (Cls.default) { 
            Cls = Cls.default;
        }
        //加载具体一个类
        let cls = new Cls();
        // console.debug('load cls', cls);
        let clsPrefix = prefix + '/' + Cls.name.toLowerCase();
        // console.debug("clsPrefix==>", clsPrefix);
        loadCls(clsPrefix, cls, cls);
    }
}

/**
 * 
 * @param prefix 前缀字符串
 * @param cls   要遍历的class instance
 * @param thisArg 加载原型链上函数时，this需要指定为最上层类的实例
 */
function loadCls(prefix: string, cls: any, thisArg?: any) {
    let arr = Object.getOwnPropertyNames(cls);
    if (!thisArg) {
        thisArg = cls;
    }
    // console.debug("arr:", arr)
    for (let fnName of arr) { 
        if (fnName == 'constructor') { 
            continue;
        }
        if (/^\$/.test(fnName)) { 
            continue;
        }
        if (typeof fnName !== 'string') { 
            continue;
        }
        let fn = cls[fnName];
        if (typeof fn !== 'function') { 
            continue;
        }
        let bindDir = prefix + '/' + fnName;
        fns[bindDir] = fn.bind(thisArg);
    }
    //加载原型链上函数
    let proto = Object.getPrototypeOf(cls);
    if (!isObject(proto)) { 
        loadCls(prefix, proto, thisArg);
    }
}

function isObject(proto: Object) {
    let b = true;
    // let proto = Object.getPrototypeOf(obj);
    // console.debug(proto);
    if (proto && proto.constructor && proto.constructor.name != 'Object') { 
        b = false;
    }
    return b;
}

function isIgnore(filename: string, ignores?: (string | RegExp)[]) : boolean{ 
    let b = false;
    /**
     * 如果是 x.b.ts文件直接跳过
     */
    if (/\.d$/.test(filename)) { 
        return true;
    }
    if (!ignores) { 
        return b;
    }
    for (let ignore of ignores) { 
        // console.debug(ignore, filename, ignore == filename)
        if (typeof ignore == 'string' && ignore == filename) { 
            b = true;
            break;
        }
        if (ignore instanceof RegExp && ignore.test(filename)) { 
            b = true;
            break;
        }
    }
    return b;
}